package com.sinosoft.cloud.access.router;


import com.sinosoft.cloud.access.entity.Body;
import com.sinosoft.cloud.access.entity.Head;
import com.sinosoft.cloud.access.entity.TranData;
import com.sinosoft.cloud.access.net.ClientProperties;
import com.sinosoft.cloud.access.net.MessageObject;
import com.sinosoft.cloud.access.net.NettyClient;
import com.xiaoleilu.hutool.util.BeanUtil;
import io.netty.channel.ChannelHandlerContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
public class RouterBean {

    protected final Log logger = LogFactory.getLog(getClass());

    protected ExpressionParser parser = new SpelExpressionParser();


    @Autowired
    BeanFactory beanFactory;

    @Autowired
    private ClientProperties properties;

    @Autowired
    private NettyClient nettyClient;

    @Autowired
    StringRedisTemplate redisTemplate;

    public Boolean isHeadRemote(String accessName, Head head) {
        String el = getHeadEL(accessName);
        TranData tranData = new TranData();
        tranData.setHead(head);
        return isRemote(el, tranData);
    }

    public String getHeadEL(String accessName) {

        if (properties != null && properties.getRouter() != null) {
            if (properties.getRouter().get(accessName) == null) {
                return null;
            }
            return properties.getRouter().get(accessName).getHead();
        }
        return null;
    }

    public Boolean isBodyRemote(String accessName, Body body) {
        String el = getBodyEL(accessName);
        TranData tranData = new TranData();
        tranData.setBody(body);
        return isRemote(el, tranData);
    }

    public String getBodyEL(String accessName) {
        if (properties != null && properties.getRouter() != null) {
            if (properties.getRouter().get(accessName) == null) {
                return null;
            }
            return properties.getRouter().get(accessName).getBody();
        }
        return null;
    }

    public Boolean isCrossRemote(String accessName, TranData tranData) {
        String el = getCrossEL(accessName);
        return isRemote(el, tranData);
    }

    public String getCrossEL(String accessName) {
        if (properties != null && properties.getRouter() != null) {
            if (properties.getRouter().get(accessName) == null) {
                return null;
            }
            return properties.getRouter().get(accessName).getCross();
        }
        return null;
    }


    /**
     * @return
     */
    private Boolean isRemote(String el, TranData tranData) {
        Map<String, Object> map = getParams(tranData);

        if (map == null) {
            logger.error("在路由判断前应先传入 head 或 body !");
            throw new RuntimeException("在路由判断前应先传入 head 或 body");
        }

        StandardEvaluationContext headContext = new StandardEvaluationContext();
        BeanFactoryResolver beanFactoryResolver = new BeanFactoryResolver(beanFactory);
        headContext.setBeanResolver(beanFactoryResolver);
        headContext.setVariables(map);
        Expression expression = parser.parseExpression(el);
        Boolean result = expression.getValue(headContext, Boolean.class);
        return result;
    }

    private Map<String, Object> getParams(TranData tranData) {

        if (null != tranData.getHead() && null != tranData.getBody()) {
            return BeanUtil.beanToMap(tranData, false, true);
        }

        if (tranData.getHead() != null) {
            return BeanUtil.beanToMap(tranData.getHead(), false, true);
        } else if (tranData.getBody() != null) {
            return BeanUtil.beanToMap(tranData.getBody(), false, true);
        }
        return null;
    }

    public void setProperties(ClientProperties properties) {
        this.properties = properties;
    }

    public Boolean setRouterKey(String key) {
        redisTemplate.opsForValue().set("AccessRouter" + key,"1");
        return true;
    }

    public Boolean getRouterKey(String key) {
        return redisTemplate.hasKey("AccessRouter" + key);
    }


    public String getTargetIP() {
        return null;
    }

    public void sendToRouter(String accessName, ChannelHandlerContext ctx, Object msg, MessageObject messageObject) {
        nettyClient.send(accessName, ctx, msg, messageObject);
    }
}
